package fun.stgoder.test1;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestAsync2Sync {
    public static void main(String[] args) {
        String reqId = String.valueOf(System.currentTimeMillis());

        new Thread(() -> { // send
            String reqContent = UUID.randomUUID().toString();
            Req req = new Req(reqId, reqContent);
            System.out.println("call will blocked");
            try {
                req.call(10);
                System.out.println("call end received: " + req.respContent);
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> { // receive
            TestUtil.sleepSeconds(3);
            String msg = reqId + "_" + UUID.randomUUID();
            FakeSocket.receive(msg);
        }).start();
    }
}

class Req {
    public static Map<String, Req> reqMap = new ConcurrentHashMap<>();

    public String reqId;
    public String reqContent;
    public String respContent;

    private Lock lock = new ReentrantLock();
    public Condition done = lock.newCondition();
    public AtomicBoolean isDone = new AtomicBoolean(false);

    public Req(String reqId, String reqContent) {
        this.reqId = reqId;
        this.reqContent = reqContent;
    }

    public void call(int timeoutSeconds) throws TimeoutException {
        FakeSocket.send(this); // fake async socket

        reqMap.put(reqId, this);

        lock.lock();
        try {
            done.await(timeoutSeconds, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            reqMap.remove(reqId);
        }

        if (!isDone.get())
            throw new TimeoutException("call timeout");
    }

    public void callback(String respContent) {
        lock.lock();
        try {
            this.respContent = respContent;
            if (!isDone.get()) {
                done.signalAll();
                isDone.set(true);
            }
        } finally {
            lock.unlock();
        }
    }
}

class FakeSocket {
    public static void send(Req req) {

    }

    public static void receive(String msg) {
        String[] strs = msg.split("_");
        String reqId = strs[0];
        String respContent = strs[1];
        Req req = Req.reqMap.get(reqId);
        if (req != null) {
            req.callback(respContent);
        }
    }
}
